package com.cscec5b.inspection.service;

import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
//import org.w3c.dom.Document;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

import com.openhtmltopdf.extend.FSSupplier;
//import com.openhtmltopdf.pdfboxout.PdfRendererBuilder.FontStyle;


import org.jsoup.Jsoup;
import org.jsoup.helper.W3CDom;
import org.jsoup.nodes.Document;


/**
 * 简单模板引擎（{{key}} 占位、{{#each checklist}} 块） + openhtmltopdf 渲染 PDF
 */
@Service
public class PdfRenderService {

    /** 渲染：仅使用系统/内置字体（可能不显示中文） */
    public byte[] render(String templateClasspath, Map<String, Object> data) {
        return renderInternal(templateClasspath, data, null, null);
    }

    /** 渲染：注册中文字体（推荐）。fontPath 类路径，如 "fonts/NotoSansSC-Regular.otf" */
    public byte[] renderWithFont(String templateClasspath, Map<String, Object> data,
                                 String fontPath, String fontFamilyName) {
        return renderInternal(templateClasspath, data, fontPath, fontFamilyName);
    }

    /** 去掉 UTF-8 BOM 与 <html> 前的噪声字符 */
    private String sanitizeHtml(String s) {
        if (s == null) return "";
        // 去 BOM 字符 U+FEFF
        if (s.length() > 0 && s.charAt(0) == '\uFEFF') s = s.substring(1);
        s = s.replace("\uFEFF", "");
        // 找到第一个 '<'，丢弃之前的任何字符（包括可能的不可见字符/注释）
        int firstLt = s.indexOf('<');
        if (firstLt > 0) s = s.substring(firstLt);
        return s;
    }
    /** 去掉 BOM、任何 Processing Instruction(<? ... ?>)、DOCTYPE，并从 <html 开始 */
//    private String sanitizeHtml(String s) {
//        if (s == null) return "";
//
//        // 去 BOM（文件头或中间的 U+FEFF）
//        if (!s.isEmpty() && s.charAt(0) == '\uFEFF') s = s.substring(1);
//        s = s.replace("\uFEFF", "");
//
//        // 去掉任意位置的 Processing Instruction（包括 <?xml ...?>、<?xml-stylesheet ...?> 等）
//        s = s.replaceAll("(?is)<\\?[^>]*\\?>", "");
//
//        // 去掉 DOCTYPE
//        s = s.replaceFirst("(?is)<!DOCTYPE[^>]*>", "");
//
//        // 去掉 <html> 之前的任何字符（空白/注释等），确保以 <html 开始
//        int idx = s.toLowerCase().indexOf("<html");
//        if (idx > 0) s = s.substring(idx);
//
//        return s;
//    }

    private static String esc(Object v) {
        if (v == null) return "";
        String s = String.valueOf(v);
        StringBuilder out = new StringBuilder(s.length() + 16);
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            switch (c) {
                case '&': out.append("&amp;");  break;
                case '<': out.append("&lt;");   break;
                case '>': out.append("&gt;");   break;
                case '"': out.append("&quot;"); break;
                case '\'': out.append("&#39;"); break;
                default:   out.append(c);
            }
        }
        return out.toString();
    }


    // ---------- 内部实现 ----------
    @SuppressWarnings("unchecked")
    private byte[] renderInternal(String templateClasspath, Map<String, Object> data,
                                  String fontPathOrNull, String fontFamilyOrNull) {
        try {
            // 1) 读取模板
            String html = readClasspathText(templateClasspath);

            // ★★★ 关键：去 BOM / 去掉 <html> 之前的任何字符
            html = sanitizeHtml(html);


            // 2) 处理 each 区块（目前仅 checklist）
//            html = renderEachBlock(html, "checklist", (List<Map<String, Object>>) data.get("checklistData"));
            html = renderEachBlock(html, "checklist", (List<Map<String, Object>>) data.get("checklist"));

            // 3) 普通 {{key}} 替换
//            for (Map.Entry<String, Object> e : data.entrySet()) {
//                String k = "{{" + e.getKey() + "}}";
//                html = html.replace(k, e.getValue() == null ? "" : String.valueOf(e.getValue()));
//            }

            for (Map.Entry<String, Object> e : data.entrySet()) {
                String k = "{{" + e.getKey() + "}}";
                html = html.replace(k, esc(e.getValue()));
            }


            // 4) 生成 PDF
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            PdfRendererBuilder builder = new PdfRendererBuilder();
//            builder.useFastMode();
//            builder.withHtmlContent(html, null);
//            builder.toStream(baos);

            // 渲染出空白页
//            builder.useFastMode();
//
//// 用 Jsoup 先按 HTML 宽松解析，再转成 W3C DOM 给 openhtmltopdf
//            Document jsoup = Jsoup.parse(html);
//            jsoup.outputSettings().syntax(Document.OutputSettings.Syntax.xml); // 标准化为 xhtml
//            org.w3c.dom.Document w3c = new W3CDom().fromJsoup(jsoup);
//
//            builder.withW3cDocument(w3c, null);
//            builder.toStream(baos);
//
//            // 字体（可选）
//            if (fontPathOrNull != null && !fontPathOrNull.isEmpty()) {
//                final String p = fontPathOrNull.startsWith("/") ? fontPathOrNull.substring(1) : fontPathOrNull;
//                final String family = (fontFamilyOrNull == null || fontFamilyOrNull.isEmpty()) ? "custom" : fontFamilyOrNull;
//
//                builder.useFont(() -> {
//                    // 不抛受检异常的方式获取资源流
//                    InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(p);
//                    if (in == null) {
//                        throw new IllegalArgumentException("Font not found on classpath: " + p);
//                    }
//                    return in; // openhtmltopdf 会负责关闭
//                }, family);
//            }
//
//            builder.run();
//            return baos.toByteArray();

            builder.useFastMode();
            builder.withHtmlContent(html, null);   // ← 回到 HTML 路径
            builder.toStream(baos);

            // 字体（注意用 .otf/.ttf，而不是 .ttc）
            if (fontPathOrNull != null && !fontPathOrNull.isEmpty()) {
                final String p = fontPathOrNull.startsWith("/") ? fontPathOrNull.substring(1) : fontPathOrNull;
                final String family = (fontFamilyOrNull == null || fontFamilyOrNull.isEmpty()) ? "custom" : fontFamilyOrNull;
                builder.useFont(() -> {
                    InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(p);
                    if (in == null) throw new IllegalArgumentException("Font not found on classpath: " + p);
                    return in;
                }, family);
            }
            builder.run();
            return baos.toByteArray();

        } catch (Exception e) {
            throw new RuntimeException("PDF 生成失败: " + e.getMessage(), e);
        }
    }

    private String readClasspathText(String classpath) throws IOException {
        ClassPathResource r = new ClassPathResource(classpath);
        try (InputStream in = r.getInputStream()) {
            byte[] all = in.readAllBytes();
            return new String(all, StandardCharsets.UTF_8);
        }
    }

    /** 渲染 {{#each name}} ... {{/each}}。本例仅用于 checklist */
    private String renderEachBlock(String html, String eachName, java.util.List<java.util.Map<String,Object>> items) {
        if (html == null) return "";
        String pattern = "\\{\\{#each\\s+" + java.util.regex.Pattern.quote(eachName) + "\\s*\\}\\}([\\s\\S]*?)\\{\\{/each\\}\\}";
        java.util.regex.Pattern p = java.util.regex.Pattern.compile(pattern, java.util.regex.Pattern.CASE_INSENSITIVE);
        java.util.regex.Matcher m = p.matcher(html);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            String tpl = m.group(1);
            StringBuilder out = new StringBuilder();
            if (items != null) {
                for (java.util.Map<String,Object> it : items) {
                    String row = tpl;
                    for (java.util.Map.Entry<String,Object> e : it.entrySet()) {
                        String k = "{{" + e.getKey() + "}}";
//                        row = row.replace(k, e.getValue()==null? "" : String.valueOf(e.getValue()));
                        row = row.replace(k, esc(e.getValue()));    
                    }
                    out.append(row);
                }
            }
            m.appendReplacement(sb, java.util.regex.Matcher.quoteReplacement(out.toString()));
        }
        m.appendTail(sb);
        return sb.toString();
    }


    private String safe(Object o) { return o == null ? "" : String.valueOf(o); }
}
